/*
 * @Author: EltonLi lijiaming@uavi-tech.com
 * @Date: 2024-03-29 15:32:48
 * @LastEditors: lvgrape lvgrape@outlook.com
 * @LastEditTime: 2024-05-15 15:14:11
 * @LastEditors: lvgrape lvgrape@outlook.com
 * @LastEditTime: 2024-04-23 18:56:47
 * @FilePath: \PREMIUM_RTThread_V1.0\drivers\common\drv_soft_spi.c
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include "drv_common.h"
#include "drv_gpio.h"
#include "spi-bit-ops.h"
#include "rtconfig.h"
#include "drv_soft_spi.h"
#include <string.h>

#define LOG_TAG "drv.sspi"
// #define DBG_LVL DBG_LOG
#include <rtdbg.h>

#ifdef BSP_USING_RT_SSPI
static const struct at32_soft_spi_config soft_spi_config[] =
    {
#ifdef BSP_USING_SSPI
        SSPI_BUS_CONFIG,
#endif
};

static struct at32_sspi sspi_obj[sizeof(soft_spi_config) / sizeof(soft_spi_config[0])];

static void at32_sspi_gpio_init(struct at32_sspi *sspi)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)sspi->ops.data;

    rt_pin_mode(cfg->clk, PIN_MODE_OUTPUT);
    rt_pin_mode(cfg->mosi, PIN_MODE_OUTPUT);
    rt_pin_mode(cfg->miso, PIN_MODE_INPUT);

    // rt_pin_write(cfg->clk, PIN_LOW);
    // rt_pin_write(cfg->mosi, PIN_LOW);
    // rt_pin_write(cfg->miso, PIN_LOW);
}

static void sspi_tog_sclk(void *data)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)data;

    if (rt_pin_read(cfg->clk) == SET)
    {
        rt_pin_write(cfg->clk, PIN_LOW);
    }
    else
    {
        rt_pin_write(cfg->clk, PIN_HIGH);
    }
}
static void sspi_set_sclk(void *data, rt_int32_t state)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)data;
    rt_pin_write(cfg->clk, state);
    // if (state)
    // {
    //     rt_pin_write(cfg->clk, PIN_HIGH);
    // }
    // else
    // {
    //     rt_pin_write(cfg->clk, PIN_LOW);
    // }
}

static void sspi_set_mosi(void *data, rt_int32_t state)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)data;
    rt_pin_write(cfg->mosi, state);
    // if (state)
    // {
    //     rt_pin_write(cfg->mosi, PIN_HIGH);
    // }
    // else
    // {
    //     rt_pin_write(cfg->mosi, PIN_LOW);
    // }
}

static void sspi_set_miso(void *data, rt_int32_t state)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)data;
    rt_pin_write(cfg->miso, state);
    // if (state)
    // {
    //     rt_pin_write(cfg->miso, PIN_HIGH);
    // }
    // else
    // {
    //     rt_pin_write(cfg->miso, PIN_LOW);
    // }
}

static rt_int32_t sspi_get_sclk(void *data)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)data;
    return rt_pin_read(cfg->miso);
}

static rt_int32_t sspi_get_mosi(void *data)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)data;
    return rt_pin_read(cfg->mosi);
}

static rt_int32_t sspi_get_miso(void *data)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)data;
    return rt_pin_read(cfg->miso);
}

static void sspi_dir_mosi(void *data, rt_int32_t state)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)data;
    if (state)
    {
        rt_pin_mode(cfg->mosi, PIN_MODE_INPUT_PULLUP);
    }
    else
    {
        rt_pin_mode(cfg->mosi, PIN_MODE_OUTPUT_OD);
    }
}

static void sspi_dir_miso(void *data, rt_int32_t state)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)data;
    if (state)
    {
        rt_pin_mode(cfg->miso, PIN_MODE_INPUT_PULLUP);
    }
    else
    {
        rt_pin_mode(cfg->miso, PIN_MODE_OUTPUT_OD);
    }
}

static void sspi_udelay(rt_uint32_t us)
{
    rt_uint32_t ticks;
    rt_uint32_t told, tnow, tcnt = 0;
    rt_uint32_t reload = SysTick->LOAD;

    ticks = us * reload / (1000000 / RT_TICK_PER_SECOND);
    told = SysTick->VAL;
    while (1)
    {
        tnow = SysTick->VAL;
        if (tnow != told)
        {
            if (tnow < told)
            {
                tcnt += told - tnow;
            }
            else
            {
                tcnt += reload - tnow + told;
            }
            told = tnow;
            if (tcnt >= ticks)
            {
                break;
            }
        }
    }
}

static struct rt_spi_bit_ops at32_sspi_bit_ops_default =
    {
        .data = RT_NULL,
        .tog_sclk = sspi_tog_sclk,
        .set_sclk = sspi_set_sclk,
        .set_mosi = sspi_set_mosi,
        .set_miso = sspi_set_miso,
        .get_sclk = sspi_get_sclk,
        .get_mosi = sspi_get_mosi,
        .get_miso = sspi_get_miso,
        .dir_mosi = sspi_dir_mosi,
        .dir_miso = sspi_dir_miso,
        .udelay = sspi_udelay,
};

rt_err_t rt_sf_spi_device_attach(const char *bus_name, const char *device_name, rt_base_t pinnum)
{
    rt_err_t result;
    struct rt_spi_device *spi_device;
    rt_base_t *cs_pin;

    rt_pin_mode(pinnum, PIN_MODE_OUTPUT);
    rt_pin_write(pinnum, PIN_LOW);

    /* attach the device to spi bus */
    spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
    RT_ASSERT(spi_device != RT_NULL);
    cs_pin = (rt_base_t *)rt_malloc(sizeof(rt_base_t));
    RT_ASSERT(cs_pin != RT_NULL);
    *cs_pin = pinnum;
    result = rt_spi_bus_attach_device(spi_device, device_name, bus_name, (void *)cs_pin);
    if (result != RT_EOK)
    {
        LOG_D("%s attach to %s faild, %d\n", device_name, bus_name, result);
    }

    RT_ASSERT(result == RT_EOK);

    LOG_D("%s attach to %s done", device_name, bus_name);

    return result;
}

int rt_soft_spi_init(void)
{
    rt_size_t obj_num = sizeof(sspi_obj) / sizeof(struct at32_sspi);
    rt_err_t result;
    for (int i = 0; i < obj_num; i++)
    {
        sspi_obj[i].ops.data = (void *)&soft_spi_config[i];
        at32_sspi_bit_ops_default.data = (void *)&soft_spi_config[i];
        at32_sspi_gpio_init(&sspi_obj[i]);
        result = rt_spi_bit_add_bus(&(sspi_obj[i].spi_bus), soft_spi_config[i].bus_name, &at32_sspi_bit_ops_default);
        RT_ASSERT(result == RT_EOK);

        LOG_D("software simulation %s init done, pin clk: %d, pin miso %d,pin miso %d\n",
              soft_spi_config[i].bus_name,
              soft_spi_config[i].clk,
              soft_spi_config[i].miso,
              soft_spi_config[i].mosi);
    }
}
#endif

#ifndef __NOP
#define __NOP() __asm__ __volatile__("nop")
#endif // !__NOP()

#define __io_set(x) gpio_bits_set(bus->ios->x.port, bus->ios->x.pin)
#define __io_reset(x) gpio_bits_reset(bus->ios->x.port, bus->ios->x.pin)
// #define __io_read(x) ((bus->ios->x.port->idt & bus->ios->x.pin) != 0)
#define __io_read(x) gpio_input_data_bit_read(bus->ios->x.port, bus->ios->x.pin)
#define __io_write(x, y) \
    if (y)               \
        __io_set(x);     \
    else                 \
        __io_reset(x)

static rt_err_t soft_spi_configure(struct rt_spi_device *device,
                                   struct rt_spi_configuration *configuration)
{
    struct rt_spi_bus *spi_bus = (struct rt_spi_bus *)device->bus;
    struct soft_spi_bus *instance = (struct soft_spi_bus *)spi_bus->parent.user_data;
    // NOTE 2.5Mhz
    instance->mode = configuration->mode;
    gpio_bits_write(instance->ios->clk.port, instance->ios->clk.pin, (instance->mode & SOFT_SPI_CPOL)); // 空闲电平
    return RT_EOK;
}

void soft_spi_bus_init(struct soft_spi_bus *bus, struct soft_spi_ios *ios)
{
    gpio_init_type gpio_init_struct;

    gpio_init_struct.gpio_pins = ios->clk.pin;
    gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
    gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MAXIMUM;
    gpio_init(ios->clk.port, &gpio_init_struct);

    gpio_init_struct.gpio_pins = ios->miso.pin;
    gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
    gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
    gpio_init_struct.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN;
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MAXIMUM;
    gpio_init(ios->miso.port, &gpio_init_struct);

    gpio_init_struct.gpio_pins = ios->mosi.pin;
    gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
    gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MAXIMUM;
    gpio_init(ios->mosi.port, &gpio_init_struct);

    bus->ios = ios;
    // bus->delay_us = spi_delay;
    __io_write(clk, bus->mode & SOFT_SPI_CPOL); // 空闲电平
}

static uint8_t soft_spi_transfer(struct soft_spi_bus *bus, uint8_t data)
{
#define __write_bit()                    \
    if (bus->mode & SOFT_SPI_MSB)        \
    {                                    \
        __io_write(mosi, (data & 0x80)); \
        data <<= 1;                      \
    }                                    \
    else                                 \
    {                                    \
        __io_write(mosi, (data & 0x01)); \
        data >>= 1;                      \
    }
#define __read_bit()                                      \
    if (__io_read(miso))                                  \
    {                                                     \
        data |= (bus->mode & SOFT_SPI_MSB) ? 0x01 : 0x80; \
    }
    // LOG_D("input = %d\n", gpio_input_data_bit_read(bus->ios->miso.port, bus->ios->miso.pin));
    // SOFT_SPI_MODE0://CPOL 0, CPHA 0
    //     for (int i = 0; i < 8; i++)
    //     {
    //         __write_bit();
    //         __io_write(clk, 1);
    //         bus->delay_us(1);
    //         __read_bit();
    //         __io_write(clk, 0);
    //         bus->delay_us(1);
    //     }
    // SOFT_SPI_MODE1://CPOL 0, CPHA 1
    //     for (int i = 0; i < 8; i++)
    //     {
    //         __io_write(clk, 1);
    //         __write_bit();
    //         bus->delay_us(1);
    //         __io_write(clk, 0);
    //         bus->delay_us(1);
    //         read_bit();
    //     }

    // SOFT_SPI_MODE3://CPOL 1, CPHA 0
    //     for (int i = 0; i < 8; i++)
    //     {
    //         __write_bit();
    //         __io_write(clk, 0);
    //         bus->delay_us(1);
    //         __read_bit();
    //         __io_write(clk, 1);
    //         bus->delay_us(1);
    //     }

    // SOFT_SPI_MODE4://CPOL 1, CPHA 1
    //     for (int i = 0; i < 8; i++)
    //     {
    //         __io_write(clk, 0);
    //         __write_bit();
    //         bus->delay_us(1);
    //         __io_write(clk, 1);
    //         bus->delay_us(1);
    //         __read_bit();
    //     }
    // __io_write(clk, (bus->mode & SOFT_SPI_CPOL));
    // bus->delay_us(1);

    // data = 0x88;
    for (int i = 0; i < 8; i++)
    {
        /**
         * @brief
         * MODE CPOL    CPHA   CLK_IDL  MISO    MOSI
         * 0    0       0       0       RISING  FALLING
         * 1    0       1       0
         * 2    1       0       1
         * 3    1       1       1
         */
        if (bus->mode & SOFT_SPI_CPHA)
        {
            // NOTE CPHA=1, CPOL=X. first edge write, second edge read
            __io_write(clk, !(bus->mode & SOFT_SPI_CPOL));
            __write_bit();
            __NOP();
            // bus->delay_us(bus->clktk);
            __io_write(clk, (bus->mode & SOFT_SPI_CPOL));
            // bus->delay_us(bus->clktk);
            __NOP();
            __read_bit();
        }
        else
        {
            // NOTE CPHA=0, CPOL=X.  first edge read, second edge write
            __write_bit();
            __io_write(clk, !(bus->mode & SOFT_SPI_CPOL));
            __NOP();
            // bus->delay_us(bus->clktk);
            __read_bit();
            __io_write(clk, (bus->mode & SOFT_SPI_CPOL));
            __NOP();
            // bus->delay_us(bus->clktk);
        }
    }
    // LOG_D("input data = %04x\n",gpio_input_data_read(bus->ios->miso.port));
    LOG_D("output data = %04x\n", data);

    return data;
}

static rt_uint32_t soft_spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message)
{
    struct rt_spi_bus *at32_spi_bus = (struct rt_spi_bus *)device->bus;
    struct soft_spi_bus *bus = (struct soft_spi_bus *)at32_spi_bus->parent.user_data;
    struct io_poSOFT *cs = device->parent.user_data;

    rt_size_t message_length = 0, already_send_length = 0;
    rt_uint16_t send_length = 0;
    rt_uint8_t *recv_buf;
    const rt_uint8_t *send_buf;

    RT_ASSERT(device != NULL);
    RT_ASSERT(message != NULL);

    // rt_kprintf("device = %08x\n",device);
    /* take cs */
    if (message->cs_take)
    {
        // NOTE CS需要取反
        // gpio_bits_reset(cs->port, cs->pin);
        gpio_bits_set(cs->port, cs->pin);
        // LOG_D("spi take cs\n");
    }
    message_length = message->length;
    recv_buf = message->recv_buf;
    send_buf = message->send_buf;
    while (message_length)
    {
        /* the HAL library use uint16 to save the data length */
        if (message_length > 65535)
        {
            send_length = 65535;
            message_length = message_length - 65535;
        }
        else
        {
            send_length = message_length;
            message_length = 0;
        }

        /* calculate the start address */
        already_send_length = message->length - send_length - message_length;
        send_buf = (rt_uint8_t *)message->send_buf + already_send_length;
        recv_buf = (rt_uint8_t *)message->recv_buf + already_send_length;
        /* start once data exchange in dma mode */
        if (message->send_buf && message->recv_buf)
        {

            for (uint8_t i = 0; i < send_length; i++)
            {
                recv_buf[i] = soft_spi_transfer(bus, send_buf[i]);
            }
        }
        else if (message->send_buf)
        {
            for (uint8_t i = 0; i < send_length; i++)
            {
                LOG_D("****SEND****\n");
                LOG_D("soft spi send data:%02x\n", send_buf[i]);
                soft_spi_transfer(bus, send_buf[i]);
            }
        }
        else
        {
            memset((void *)recv_buf, 0xff, send_length);
            for (uint8_t i = 0; i < send_length; i++)
            {
                LOG_D("****READ****\n");
                recv_buf[i] = soft_spi_transfer(bus, 0x00);
                LOG_D("soft spi read data:%02x\n", recv_buf[i]);
            }
        }
    }
    /* release cs */
    if (message->cs_release)
    {
        gpio_bits_reset(cs->port, cs->pin);
    }

    return message->length;
}

static struct rt_spi_ops at32_soft_spi_ops =
    {
        soft_spi_configure,
        soft_spi_xfer};

int at_sf_spi_init(void)
{
    rt_err_t result;
    struct rt_spi_bus *rt_soft_spi_bus = RT_NULL;
    rt_soft_spi_bus = (struct rt_spi_bus *)rt_malloc(sizeof(struct rt_spi_bus));

    static soft_spi_bus_t soft_spi_bus = {
        .mode = SOFT_SPI_MODE_3 | SOFT_SPI_MSB};
    static soft_spi_ios_t soft_spi_bus_io =
        {
            .mosi = {GPIOB, GPIO_PINS_10},
            .miso = {GPIOA, GPIO_PINS_2},
            .clk = {GPIOA, GPIO_PINS_3},
        };

    soft_spi_bus_init(&soft_spi_bus, &soft_spi_bus_io);

    rt_soft_spi_bus->parent.user_data = &soft_spi_bus;

    result = rt_spi_bus_register(rt_soft_spi_bus, "sspi", &at32_soft_spi_ops);
    LOG_D("sspi init result:%d\n", result);
    return result;
}



// INIT_BOARD_EXPORT(rt_soft_spi_init);
INIT_BOARD_EXPORT(at_sf_spi_init);